package com.jtny.nytb.config.excel.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.jtny.component.exception.ServiceException;
import com.jtny.component.util.BeanUtils;
import com.jtny.nytb.config.excel.listener.ExcelListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * easyExcel工具类
 *
 * @author waz
 * @version 1.0
 * @package com.jtny.nytb.util
 * @fileName EasyExcelUtils.java
 * @createTime 2023年08月15日 13:30
 * @Copyright© 2023 jtny
 * 江投能源技术研究院 版权所有
 */
@Slf4j
public class EasyExcelUtils {

    /**
     * 导出excel
     *
     * @param response 响应头
     * @param fileName 文件名称
     * @param list     数据列表
     * @param clazz    导出表头类
     */
    public static <T> void exportExcel(HttpServletResponse response, String fileName, List<T> list, Class clazz) {
        //获取写输出流
        ExcelWriter excelWriter = null;

        try {
            excelWriter = EasyExcel.write(getOutputStream(response, fileName)).build();

            //设置一个sheet对象： 第一个参数标识 第一个sheet ,第二个参数 工作簿名称。 设置表头
            WriteSheet sheet = EasyExcel.writerSheet(0, "sheet1").head(clazz).build();

            //开始写
            excelWriter.write(list, sheet);
        } catch (Exception e) {
            log.error(e.getLocalizedMessage());
            throw new RuntimeException("excel导出异常: " + e.getLocalizedMessage());
        } finally {
            //完毕，会自动关闭输出流
            excelWriter.finish();
        }
    }

    /**
     * 导出 excel 多sheet 页
     *
     * @param response  响应
     * @param fileName  文件名
     * @param sheetList sheet 页
     */
    public static void exportExcelManySheet(HttpServletResponse response, String fileName, List<ExcelSheetVO> sheetList) {
        //获取写输出流
        ExcelWriter excelWriter = null;

        try {
            excelWriter = EasyExcel.write(getOutputStream(response, fileName)).build();
            for (int i = 0; i < sheetList.size(); i++) {
                ExcelSheetVO excelSheet = sheetList.get(i);
                //设置一个sheet对象： 第一个参数标识 第一个sheet ,第二个参数 工作簿名称。 设置表头
                WriteSheet sheet = EasyExcel.writerSheet(i, excelSheet.getSheetName()).head(excelSheet.getClazz()).build();
                //开始写
                excelWriter.write(excelSheet.getValueList(), sheet);
            }
        } catch (Exception e) {
            log.error(e.getLocalizedMessage());
            throw new RuntimeException("excel导出异常: " + e.getLocalizedMessage());
        } finally {
            //完毕，会自动关闭输出流
            excelWriter.finish();
        }
    }

    /**
     * 导出文件时为Writer生成OutputStream
     */
    private static OutputStream getOutputStream(HttpServletResponse response, String fileName) throws Exception {
        try {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf8");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
            response.setHeader("Pragma", "public");
            response.setHeader("Cache-Control", "no-store");
            response.addHeader("Cache-Control", "max-age=0");
            return response.getOutputStream();
        } catch (IOException e) {
            throw new Exception("导出excel表格失败!", e);
        }
    }

    /**
     * 根据Excel模板，批量导入数据
     *
     * @param file  导入的Excel
     * @param clazz 解析的类型
     * @return 解析完成的数据
     */
    public static <T> List<T> importExcel(MultipartFile file, Class<T> clazz) {
        if (file == null || file.isEmpty()) {
            throw new RuntimeException("没有文件或者文件内容为空！");
        }
        List<T> dataList = null;
        BufferedInputStream ipt = null;
        try {
            InputStream is = file.getInputStream();
            // 用缓冲流对数据流进行包装
            ipt = new BufferedInputStream(is);
            // 数据解析监听器
            ExcelListener listener = new ExcelListener();
            // 读取数据
            EasyExcel.read(ipt, clazz, listener).headRowNumber(1).sheet().doRead();
            // 获取去读完成之后的数据
            dataList = listener.getDataList();
        } catch (Exception e) {
            log.error(String.valueOf(e));
            throw new RuntimeException("数据导入失败！" + e);
        }
        return dataList;
    }


    public static Map<Class<?>, List<?>> importExcelManySheet(MultipartFile file, List<ExcelSheetVO> sheetList) throws IOException {
        if (file == null || file.isEmpty()) {
            throw new ServiceException("没有文件或者文件内容为空！");
        }
        Map<Class<?>, List<?>> dataMap = new HashMap<>();
        BufferedInputStream ipt = null;
        ExcelReader excelReader = null;
        try {
            List<?> dataList = null;
            InputStream is = file.getInputStream();
            // 用缓冲流对数据流进行包装
            ipt = new BufferedInputStream(is);
            excelReader = EasyExcel.read(ipt).build();
            for (int i = 0; i < sheetList.size(); i++) {
                // 读取数据
                Class clazz = sheetList.get(i).getClazz();

                ExcelListener<Object> excelListener = new ExcelListener<>();
                ReadSheet readSheet = EasyExcel.readSheet(i).head(clazz).registerReadListener(excelListener).build();
                excelReader.read(readSheet);
                // 获取去读完成之后的数据
                dataList = excelListener.getDataList();
                dataMap.put(sheetList.get(i).getClazz(), BeanUtils.copyToList(dataList, sheetList.get(i).getClazz()));
                excelListener.getDataList().clear();
            }
        } catch (Exception e) {
            log.error(String.valueOf(e));
            throw new ServiceException("数据导入失败！" + e);
        } finally {
            assert ipt != null;
            ipt.close();
            // 读的时候会创建临时文件，到时磁盘会崩
            assert excelReader != null;
            excelReader.finish();
            excelReader.close();
        }
        return dataMap;
    }
}